﻿using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Text;
using System.Configuration;

using BizElements.BusinessLayer;

namespace BizElements.Web
{
    /// <summary>
    /// <see cref="DataGrid"/> template which displays and edits the value of a bound field in a <see cref="BoundDropDownList"/> control.
    /// </summary>
    public class BoundDropDownListTemplate : BoundTemplateBase
    {
        #region Fields.

        bool nullItemEnabled = true;
        string nullItemValue = string.Empty;
        string nullItemText = string.Empty;

        #endregion

        #region Constructors.

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundDropDownListTemplate"/> class.
        /// </summary>
        public BoundDropDownListTemplate()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundDropDownListTemplate"/> class.
        /// </summary>
        /// <param name="field"><see cref="IBindableField"/> that contains a reference and meta-data for a bound business class property.</param>
        /// <param name="formatter">The <see cref="IMessageFormatter"/> object used to format bound values.</param>
        public BoundDropDownListTemplate(IBindableField field, IMessageFormatter formatter)
            : base(field, formatter)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundDropDownListTemplate"/> class.
        /// </summary>
        /// <param name="field"><see cref="IBindableField"/> that contains a reference and meta-data for a bound business class property.</param>
        /// <param name="formatter">The <see cref="IMessageFormatter"/> object used to format bound values.</param>
        /// <param name="skinID">The skin to apply to the controls generated by this template.</param>
        public BoundDropDownListTemplate(IBindableField field, IMessageFormatter formatter, string skinID)
            : base(field, formatter, skinID)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundDropDownListTemplate"/> class.
        /// </summary>
        /// <param name="field"><see cref="IBindableField"/> that contains a reference and meta-data for a bound business class property.</param>
        /// <param name="formatter">The <see cref="IMessageFormatter"/> object used to format bound values.</param>
        /// <param name="width">The width of the drop down list used in the template.</param>
        /// <param name="cssClass">CSS Class name applied to drop down list created by the template.</param>
        /// <param name="readOnlyCss">CSS Class name applied to controls created by the template when in read-only mode.</param>
        public BoundDropDownListTemplate(IBindableField field, IMessageFormatter formatter, int width, string cssClass, string readOnlyCss)
            : base(field, formatter, width, cssClass, readOnlyCss)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BoundDropDownListTemplate"/> class.
        /// </summary>
        /// <param name="field"><see cref="IBindableField"/> that contains a reference and meta-data for a bound business class property.</param>
        /// <param name="formatter">The <see cref="IMessageFormatter"/> object used to format bound values.</param>
        /// <param name="skinID">The skin to apply to the controls generated by this template.</param>
        /// <param name="nullItemEnabled">Whether a default/null-value <see cref="ListItem"/> is rendered at zero index.</param>
        public BoundDropDownListTemplate(IBindableField field, IMessageFormatter formatter, string skinID, bool nullItemEnabled)
            : base(field, formatter, skinID)
        {
            this.nullItemEnabled = nullItemEnabled;
        }

        #endregion

        #region Methods.

        /// <summary>
        /// Defines the <see cref="DataGrid"/> that child drop down list controls and the template belong to.
        /// </summary>
        /// <param name="container">The <b>TableCell</b> which contains the drop down list controls instantiated by this template.</param>
        public override void InstantiateIn(Control container)
        {
            BoundDropDownList ddl = new BoundDropDownList();
            ddl.DataBinding += new EventHandler(this.BindCodeList);
            ddl.DataBound += new EventHandler(this.SetSelectedValue);
            if (this.SkinID != null)
            {
                ddl.SkinID = this.SkinID;
                Page page = HttpContext.Current.Handler as Page;
                if (page != null)
                    ddl.ApplyStyleSheetSkin(page);
            }

            container.Controls.Add(ddl);
        }

        private void BindCodeList(object sender, EventArgs e)
        {
            BoundDropDownList ddl = (BoundDropDownList)sender;
            ApplyStyle(ddl);

            ddl.DataSource = this.BoundField.CodeList.DataSource;
            ddl.DataValueField = this.BoundField.CodeList.ValueMember;
            ddl.DataTextField = this.BoundField.CodeList.DisplayMember;

            bool isReadOnly = ConfigureAccessMode(ddl);
            if (isReadOnly)
            {
                ddl.BackColor = Color.Transparent;
                ddl.Style["cursor"] = "pointer";
            }
        }

        private void SetSelectedValue(object sender, EventArgs e)
        {
            BoundDropDownList ddl = (BoundDropDownList)sender;
            DataGridItem currentItem = (DataGridItem)ddl.NamingContainer;
            object val = DataBinder.Eval(currentItem.DataItem, this.BoundField.BindingName);

            EnsureNullItem(ddl);
            if (val != null)
                ddl.SelectedValue = val.ToString();
            else
                ddl.SelectedIndex = 0;
        }

        private void EnsureNullItem(BoundDropDownList ddl)
        {
            if (!this.NullItemEnabled)
                return;

            bool alreadyHasNullItem = (ddl.Items.FindByValue(this.NullItemValue) != null);
            if (alreadyHasNullItem)
                return;

            ddl.Items.Insert(0, new ListItem(this.NullItemText, this.NullItemValue));
        }

        #endregion

        #region Properties.

        /// <summary>
        /// Gets or sets whether a default/null-value <see cref="ListItem"/> is rendered at zero index.
        /// </summary>
        /// <value><b>true</b> if enabled; <b>false</b> otherwise. Default is <b>true</b>.</value>
        public virtual bool NullItemEnabled
        {
            get { return nullItemEnabled; }
            set { nullItemEnabled = value; }
        }

        /// <summary>
        /// Gets or sets the <see cref="ListItem.Value"/> of default/null-value <see cref="ListItem"/>.
        /// </summary>
        /// <value>A string. Default is <see cref="String.Empty"/>.</value>
        public virtual string NullItemValue
        {
            get { return nullItemValue; }
            set { nullItemValue = value; }
        }

        /// <summary>
        /// Gets or sets the <see cref="ListItem.Text"/> of default/null-value <see cref="ListItem"/>.
        /// </summary>
        /// <value>A string. Default is <see cref="String.Empty"/>.</value>
        public virtual string NullItemText
        {
            get { return nullItemText; }
            set { nullItemText = value; }
        }

        #endregion
    }
}